package prod_cons;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.junit.Assert;
import org.junit.Test;

public class TreesTest {
	private static class DumbTask implements Runnable {
		@Override
		public void run() {
		}
	}

	private static final int NUM_TASKS = 800;
	private static final int NUM_THREADS = 32;
	
	@Test
	public void seq_presenceCheck() {
		TaskContainer tainer = new TreesContainer();
		int tries = 0;
		Set<Runnable> tasks = new HashSet<Runnable>(), outTasks = new HashSet<Runnable>();
		for (int i = 0; i < NUM_TASKS; i++) {
			tasks.add(new DumbTask());
		}
		for (Runnable task : tasks) {
			tainer.put(task);
		}
		for (int i = 0; i < NUM_TASKS; i++) {
			Runnable task = tainer.get();
			if (task != null)
				outTasks.add(task);
			else {
				tries++;
				i--;
			}
		}
		Assert.assertTrue(outTasks.size() == tasks.size());
		for (Runnable task : tasks) {
			Assert.assertTrue(outTasks.contains(task));
		}
		System.out.println("Seq test successful with " + tries + " retries. :)");
	}
	
	// Parallel test
	private static class InserterRunnable<CONTAINER extends TaskContainer> implements Runnable {
		CONTAINER tree;
		Runnable task;

		public InserterRunnable(CONTAINER t, Runnable r) {
			tree = t;
			task = r;
		}

		@Override
		public void run() {
			tree.put(task);
		}
	}

	private static class RemoverRunnable<CONTAINER extends TaskContainer> implements Runnable {
		CONTAINER tree;
		Runnable task = null;
		int retries;
		
		public RemoverRunnable(CONTAINER t) {
			tree = t;
		}

		@Override
		public void run() {
			task = tree.get();
		}
		
		public Runnable getTask() {
			return task;
		}
		
		public int getRetries() {
			return retries;
		}
	}
	
	@Test
	public void par_presenceCheck() throws InterruptedException {
		TaskContainer tree = new TreesContainer();
		Set<Runnable> tasksIn = new HashSet<Runnable>();
		for (int i = 0; i < NUM_TASKS; i++) {
			tasksIn.add(new DumbTask());
		}

		ExecutorService e1 = Executors.newFixedThreadPool(NUM_THREADS / 2);
		ExecutorService e2 = Executors.newFixedThreadPool(NUM_THREADS / 2);
		Runnable inserters[] = new Runnable[NUM_TASKS];
		@SuppressWarnings("unchecked")
		RemoverRunnable<TaskContainer> removers[] = new RemoverRunnable[NUM_TASKS];
		int i = 0;
		for (Runnable t : tasksIn) {
			removers[i] = new RemoverRunnable<TaskContainer>(tree);
			inserters[i++] = new InserterRunnable<TaskContainer>(tree, t);
		}
		for (Runnable ins : inserters)
			e1.execute(ins);
		e1.shutdown();
		e1.awaitTermination(300, TimeUnit.DAYS);
		for (Runnable rem : removers)
			e2.execute(rem);
		e2.shutdown();
		e2.awaitTermination(300, TimeUnit.DAYS);
		Set<Runnable> tasksOut = new HashSet<Runnable>();
		int retries = 0;
		for (RemoverRunnable<TaskContainer> r : removers) {
			tasksOut.add(r.getTask());
			retries += r.getRetries();
		}
		
		for (Runnable t : tasksIn) {
			Assert.assertTrue(tasksOut.contains(t));
		}
		System.out.println("Par test successful with " + retries + " retries. :)");
	}
}
